#include <iostream>
#include <cstring>
#include <cstdlib>
#include "net.h"
#include "base64.h"

using std::cerr; using std::endl;

Network* network = NULL;

/*---------------------------------

Network::Network
Konstruktor

---------------------------------*/

Network::Network( bool isClient, Uint16 port, const char* ip ) : port(port), isClient(isClient) {
	if( network ) delete network; network = this;

	// Initiera SDLNet
	if( SDLNet_Init() == -1) {
		cerr << "Fatalt Fel (Network::Network): SDLNet_Init misslyckades: " << SDLNet_GetError() << endl;
	}

	// Fel vid resolvandet av hosten
	if ( SDLNet_ResolveHost( &ipAddress, ip, port ) < 0 ) {
		sdlneterror;
		delete network;
		return;
	}

	// Kunde inte öppna connection till remote-server-socket/egen-server-socket
	if ( !( (isClient ? remoteSocket : serverSocket) = SDLNet_TCP_Open( &ipAddress )) ) {
		sdlneterror;
		delete network;
		return;
	}

	// Skapa tråd för att ta emot data
	if ( isClient ) {
		goto create_fetch_data_thread;
	}

	// Om vi (server) för connection, skapa tråd som tar emot data.
	for(;;) {
		if( (remoteSocket = SDLNet_TCP_Accept( serverSocket )) ) {
			create_fetch_data_thread:
			if( !( thread = SDL_CreateThread( Network::fetchDataWrapper, this ) ) ) {
				sdlerror;
			}
			return;
		}
		SDL_Delay(15);
	}
}

/*---------------------------------

int Network::fetchDataWrapper( void* n )
För att somliga är coola (??) och kör GCC från Forntiden (C)

---------------------------------*/

int Network::fetchDataWrapper( void* n ) {
	return ((Network*)n)->fetchData();
}

/*---------------------------------

int Network::fetchData()
Tar emot data och hanterar den.
(måste av SDL-tekniska skäl returna int)

---------------------------------*/

int Network::fetchData() {
	for(;;) {
		Packet* buffer = new Packet();
		// Om vi tar emot ett paket, hantera det ( som ett userevent )
		if ( SDLNet_TCP_Recv( remoteSocket, buffer, 512 ) > 0 ) {
			SDL_Event event;

			event.type = SDL_USEREVENT;
			event.user.data1 = buffer;

			// Om meddelandet är "dra åt ¤#%#%", stäng ned
			if( buffer->action == QUIT ) {
				SDLNet_TCP_Close( remoteSocket );
				delete network;
				SDL_PushEvent(&event);
				break;
			}
			
			SDL_PushEvent(&event);
		}
		// Om fel/connection closed, stäng ned skräpet och breaka
		else {
			SDLNet_TCP_Close( remoteSocket );
			delete network;
			buffer->action = QUIT;

			SDL_Event event;
			event.type = SDL_USEREVENT;
			event.user.data1 = buffer;
			SDL_PushEvent(&event);
			break;
		}
	}
	return 0;
}

/*---------------------------------

void Network::sendData( const Packet* packet )
Skickar data över nätverket
Argument: Paketet som ska skickas

---------------------------------*/

void Network::sendData( const Packet* packet ) {
	SDLNet_TCP_Send( remoteSocket, (void *)packet, 512 );
}

/*---------------------------------

Network::~Network( void )
Destruktor.

---------------------------------*/

Network::~Network( void ) {
	// Om vi är server, måste vi stänga vår server-socket.
	if ( !isClient ) {
		SDLNet_TCP_Close( serverSocket );
	}
	// Avsluta SDLNet
	SDLNet_Quit();
	network = NULL;
}

Internet* internet = NULL;

Internet::Internet( const char* myName, const char* remoteName ) {
	if( internet ) delete internet; internet = this;
	
	strcpy( this->myName = (char*)malloc(strlen(myName)+1), myName );
	strcpy( this->remoteName = (char*)malloc(strlen(remoteName)+1), remoteName );
	
	// Initiera SDLNet
	if( SDLNet_Init() == -1) {
		sdlneterror;
		return;
	}
	
	// Fel vid resolvandet av hosten
	if ( SDLNet_ResolveHost( &ipAddress, "empann.mine.nu", 80 ) < 0 ) {
		sdlneterror;
		return;
	}
	
	// Kunde inte öppna connection till servern
	if ( !( receiveSocket = SDLNet_TCP_Open( &ipAddress ) ) ) {
		sdlneterror;
		return;
	}
	
	Packet p;
	p.action = NYTT_SPEL;
	p.hostsFarg = strcmp( myName, "min" ) == 0 ? 0 : 1 ;
	sendData( (const char*)&p );
	
	// Skicka en request
	char buffer[1024];
	strcpy( buffer, "GET /schackofdoomserver.php?action=receive&name=" );
	strcpy( buffer+strlen(buffer), myName );
	strcpy( buffer+strlen(buffer), " HTTP/1.1\r\nHost: empann.mine.nu\r\n\r\n" );
	SDLNet_TCP_Send( receiveSocket, buffer, strlen(buffer) );
	
	// Försök få nån respons
	//char buffer[512];
	if ( SDLNet_TCP_Recv( receiveSocket, buffer, 1024 ) > 0 ) {
		for( char* c = &buffer[0]; c != '\0' && c-buffer+3<512; c++ ) {
			if( c[0] == '\r' && c[1] == '\n' && c[2] == '\r' && c[3] == '\n' ) {
				char* decoded = new char[512];
				b64_decode( &c[9], decoded );
				SDL_Event event;
				event.type = SDL_USEREVENT;
				event.user.data1 = decoded;
				SDL_PushEvent(&event);
				if( !SDL_CreateThread( Internet::fetchDataWrapper, this ) ) {
					sdlerror;
				}
				return;
			}
		}
	}
}

Internet::~Internet() {
	free( myName );
	free( remoteName );
}

void Internet::sendData( const char* packet ) {
	TCPsocket sendSocket;
	
	// Kunde inte öppna connection till servern
	if ( !( sendSocket = SDLNet_TCP_Open( &ipAddress ) ) ) {
		sdlneterror;
		return;
	}
	
	// Skicka en request
	char buffer[1024];
	strcpy( buffer, "GET /schackofdoomserver.php?action=send&name=" );
	strcpy( buffer+strlen(buffer), remoteName );
	strcpy( buffer+strlen(buffer), "&message=" );
	b64_encode( packet, buffer+strlen(buffer) );
	//strcpy( buffer+strlen(buffer), "Tjena!" );
	strcpy( buffer+strlen(buffer), " HTTP/1.1\r\nHost: empann.mine.nu\r\n\r\n" );
	SDLNet_TCP_Send( sendSocket, buffer, strlen(buffer) );
	SDLNet_TCP_Close( sendSocket );
	
	if ( ((Packet*)packet)->action != QUIT )
		return;
	
	// Kunde inte öppna connection till servern
	if ( !( sendSocket = SDLNet_TCP_Open( &ipAddress ) ) ) {
		sdlneterror;
		return;
	}
	
	// Skicka en request
	strcpy( buffer, "GET /schackofdoomserver.php?action=send&name=" );
	strcpy( buffer+strlen(buffer), remoteName );
	strcpy( buffer+strlen(buffer), "&message=quit HTTP/1.1\r\nHost: empann.mine.nu\r\n\r\n" );
	SDLNet_TCP_Send( sendSocket, buffer, strlen(buffer) );
	SDLNet_TCP_Close( sendSocket );
}

int Internet::fetchDataWrapper( void* n ) {
	return ((Internet*)n)->fetchData();
}

int Internet::fetchData( void ) {
	char buffer[1024];
	for(;;){
		if ( SDLNet_TCP_Recv( receiveSocket, buffer, 1024 ) > 0 ) {
			char* decoded = new char[512];
			b64_decode( &buffer[5], decoded );
			SDL_Event event;
			event.type = SDL_USEREVENT;
			event.user.data1 = decoded;
			SDL_PushEvent(&event);
		} else break;
	}
}